Skip to content

Complete CCPPization of BAM/MAM aerosol optics; BAM microp_aero (nucleate_ice; ndrop_bam; inline logic in microp_aero)#398

Open
jimmielin wants to merge 39 commits into
ESCOMP:mainfrom
jimmielin:hplin/bulk_aero_3_rebased_on_main
Open

Complete CCPPization of BAM/MAM aerosol optics; BAM microp_aero (nucleate_ice; ndrop_bam; inline logic in microp_aero)#398
jimmielin wants to merge 39 commits into
ESCOMP:mainfrom
jimmielin:hplin/bulk_aero_3_rebased_on_main

Conversation

@jimmielin

@jimmielin jimmielin commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Tag name:
Originator(s): @jimmielin

Description:

Companion CAM-SIMA PR: ESCOMP/CAM-SIMA#504

List all namelist files that were added or changed:

A  schemes/chemistry/aerosol_optics_namelist.xml
A  schemes/microp_aero/ccpp/nucleate_ice_ccpp_namelist.xml
A  schemes/microp_aero/scale_subgrid_vertical_velocity_namelist.xml
   - new scheme namelists

A  schemes/microp_aero/ccpp/hetfrz_classnuc_stub_namelist.xml
   - stub for use_hetfrz_classnuc flag (stub disables classnuc het freezing).

List all files eliminated and why: N/A

List all files added and what they do:

A  schemes/chemistry/aerosol_optics.F90
A  schemes/chemistry/aerosol_optics.meta
A  schemes/chemistry/aerosol_optics_namelist.xml
   - Aerosol optics scheme. Computes per-band SW/LW aerosol optical properties
     (extinction, SSA, asymmetry) from aerosol model instances via the abstract
     aerosol interface. Reads water refractive index data in init.

A  schemes/sima_diagnostics/aerosol_optics_diagnostics.F90
A  schemes/sima_diagnostics/aerosol_optics_diagnostics.meta
   - Aerosol optics diagnostics: per-species AOD, burden, SSA, visible AOD, etc.

A  schemes/chemistry/aerosol_optics_zero_stub.F90
A  schemes/chemistry/aerosol_optics_zero_stub.meta
   - Zero stub providing zeroed-out aerosol optical properties for RRTMGP
     snapshot testing without the full aerosol optics scheme.

A  schemes/chemistry/rebin_seasalt.F90
A  schemes/chemistry/rebin_seasalt.meta
   - Rebins 4-bin BAM prescribed seasalt (sslt01/02/03/04) to 2-bin
     (sslta/ssltc) using constituent mass fractions.

A  schemes/microp_aero/ndrop_bam.F90
   - Portable layer for bulk aerosol droplet activation (Abdul-Razzak and
     Ghan 2000). Computes droplet number tendency and CCN concentrations.
   - This is copied from CAM then slightly cleaned up

A  schemes/microp_aero/nucleate_ice.F90
   - Portable layer for ice nucleation (Liu and Penner 2005, Liu et al. 2007).
     Computes ice crystal number from homogeneous and heterogeneous nucleation.
   - This is copied from CAM

A  schemes/microp_aero/compute_subgrid_vertical_velocity.F90
A  schemes/microp_aero/compute_subgrid_vertical_velocity.meta
   - Computes subgrid vertical velocity (wsub, wsubi) from TKE or CLUBB WP2.
   - Original logic from CAM inlined in microp_aero

A  schemes/microp_aero/scale_subgrid_vertical_velocity.F90
A  schemes/microp_aero/scale_subgrid_vertical_velocity.meta
A  schemes/microp_aero/scale_subgrid_vertical_velocity_namelist.xml
   - Applies wsub_scale scaling factor to subgrid vertical velocity.
   - Original logic from CAM inlined in microp_aero

A  schemes/microp_aero/dust_default_radii.F90
A  schemes/microp_aero/dust_default_radii.meta
   - Sets default dust effective radii for radiation (dust1-4).
   - Original logic from CAM inlined in microp_aero

A  schemes/microp_aero/ccpp/ndrop_bam_ccpp.F90
A  schemes/microp_aero/ccpp/ndrop_bam_ccpp.meta
   - CCPP wrapper for ndrop_bam

A  schemes/microp_aero/ccpp/nucleate_ice_ccpp.F90
A  schemes/microp_aero/ccpp/nucleate_ice_ccpp.meta
A  schemes/microp_aero/ccpp/nucleate_ice_ccpp_namelist.xml
   - CCPP wrapper for nucleate_ice, equivalent to nucleate_ice_cam in CAM.

A  schemes/microp_aero/ccpp/hetfrz_classnuc_stub.F90
A  schemes/microp_aero/ccpp/hetfrz_classnuc_stub.meta
A  schemes/microp_aero/ccpp/hetfrz_classnuc_stub_namelist.xml
   - Stub for heterogeneous freezing (classnuc). Provides zero tendencies
     when use_hetfrz_classnuc is false. For BAM.

A  test/test_schemes/initialize_pbuf_aerosols.F90
A  test/test_schemes/initialize_pbuf_aerosols.meta
   - Test scheme for discovering aerosol number concentration species from
     pbuf-style "N:" fields via the abstract aerosol interface.

A  test/test_suites/suite_microp_aero_bam.xml
   - Test SDF for the microp_aero BAM suite.

A       schemes/sima_diagnostics/aerosol_optics_diagnostics.F90
A       schemes/sima_diagnostics/aerosol_optics_diagnostics.meta
A       schemes/sima_diagnostics/ndrop_bam_diagnostics.F90
A       schemes/sima_diagnostics/ndrop_bam_diagnostics.meta
A       schemes/sima_diagnostics/nucleate_ice_diagnostics.F90
A       schemes/sima_diagnostics/nucleate_ice_diagnostics.meta
A       schemes/sima_diagnostics/scale_subgrid_vertical_velocity_diagnostics.F90
A       schemes/sima_diagnostics/scale_subgrid_vertical_velocity_diagnostics.meta
   - diagnostic schemes

List all existing files that have been modified, and describe the changes:

M  schemes/rrtmgp/rrtmgp_sw_aerosols.F90
M  schemes/rrtmgp/rrtmgp_sw_aerosols.meta
   - Replace stub with implementation. Accepts aerosol tau/tau_w/tau_w_g from
     aerosol_optics, reorders from RRTMG to RRTMGP SW band ordering, maps CAM
     vertical levels to RRTMGP levels, subsets to daytime columns. Computes SSA
     and asymmetry parameter from the product fields.

M  schemes/rrtmgp/rrtmgp_lw_aerosols.F90
M  schemes/rrtmgp/rrtmgp_lw_aerosols.meta
   - Replace stub with implementation. Accepts aer_lw_abs from aerosol_optics,
     maps CAM vertical levels to RRTMGP levels. No band reorder needed for LW.

M  suites/suite_cam4.xml
   - Add prescribed aerosol/ozone schemes, rebin_seasalt, aerosol_optics, and
     aerosol_optics_diagnostics to CAM4 SDF
   - Reorder rrtmgp_pre before aerosol_optics so night column info is available for diagnostics.

M  test/test_suites/suite_rrtmgp.xml
   - Add aerosol_optics_zero_stub to RRTMGP test suite for snapshot testing to remain bit-for-bit.

List all automated tests that failed, as well as an explanation for why they weren't fixed:

Is this an answer-changing PR? If so, is it a new physics package, algorithm change, tuning change, etc?
New physics package
CAM regression tests for shared code
SIMA snapshot tests for microp_aero
Eyeballed the results from aerosol optics and looked reasonable.

If yes to the above question, describe how this code was validated with the new/modified features:

hplin-ucar added 21 commits May 18, 2026 12:22
…sol interface

Draft: wire up aerosol optics to rrtmgp

Move nswbands, nlwbands to host side namelist

wire up aerosol_optics in cam4 suite

to cherry-pick: remove constituent diag in prescribe_volcanic_aerosol

fix dim

diagnostic test; try to move radiative_aerosol_definitions to metadata instead of host-model dep

Fix idxnite not in rrtmgp_pre before running aerosol_optics; add ODV diags for bulk aerosol
@jimmielin jimmielin self-assigned this May 18, 2026
@jimmielin jimmielin added enhancement New feature or request aerosols Is related to aerosols (including aerosol physics and chemistry). labels May 18, 2026
Comment on lines +371 to +377
! Reset per-species accumulators for this bin/level
scatdust_d(:ncol) = 0._kind_phys; absdust_d(:ncol) = 0._kind_phys; hygrodust_d(:ncol) = 0._kind_phys
scatbc_d(:ncol) = 0._kind_phys; absbc_d(:ncol) = 0._kind_phys; hygrobc_d(:ncol) = 0._kind_phys
scatsulf_d(:ncol) = 0._kind_phys; abssulf_d(:ncol) = 0._kind_phys; hygrosulf_d(:ncol) = 0._kind_phys
scatpom_d(:ncol) = 0._kind_phys; abspom_d(:ncol) = 0._kind_phys; hygropom_d(:ncol) = 0._kind_phys
scatsoa_d(:ncol) = 0._kind_phys; abssoa_d(:ncol) = 0._kind_phys; hygrosoa_d(:ncol) = 0._kind_phys
scatsslt_d(:ncol) = 0._kind_phys; abssslt_d(:ncol) = 0._kind_phys; hygrosslt_d(:ncol) = 0._kind_phys

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note that these variables of the code is intentionally hardcoded to match CAM aerosol_optics_cam.F90 and will be refactored in the future in CAM to be namelist-driven.

hplin-ucar and others added 6 commits June 10, 2026 23:45
…ed_on_main

Portable microp_aero code (ndrop_bam, nucleate_ice, subgrid vertical
velocity and dust radii interstitials) landed in main via ESCOMP#411 with
review changes; take main's versions for all overlapping files.
Update ndrop_bam_ccpp_init for the errmsg/errflg arguments added to
ndrop_bam_init during review.
Provenance: FIX-26 octopus
Assisted-by: claude-fable:5
@nusbaume
nusbaume self-requested a review July 21, 2026 15:22
@nusbaume

Copy link
Copy Markdown
Collaborator

Automated coding-standards review

This is an automated review of the changed code against the project's Fortran coding-standards checklist (REVIEW.md). Findings are grouped by rule, with file/line references and suggested corrections. Only lines that this PR adds or modifies were checked.


Rule 7 — allocate calls must return stat and errmsg

Several newly added allocate calls omit stat/errmsg. In a physics scheme a failed allocation must set the CCPP error variables and return rather than aborting.

  • schemes/chemistry/aerosol_optics.F90:105allocate(crefwsw(nswbands))
  • schemes/chemistry/aerosol_optics.F90:122allocate(crefwlw(nlwbands))
  • schemes/microp_aero/ccpp/nucleate_ice_ccpp.F90:371allocate(size_wght(ncol, pver, nbins_, nmaxspc_))
  • schemes/microp_aero/ccpp/nucleate_ice_ccpp.F90:372allocate(amb_num_bins(ncol, pver, nbins_))
  • schemes/sima_diagnostics/aerosol_optics_diagnostics.F90:123allocate(odv_names(num_odv))
  • test/test_schemes/initialize_pbuf_aerosols.F90:94 and :98allocate(species_list(nspecies))
  • schemes/chemistry/prescribed_aerosols.F90:141 and :183allocate(aerosol_constituents(0))
  • schemes/chemistry/prescribed_ozone.F90:68allocate(ozone_constituents(0))
  • schemes/chemistry/prescribed_volcanic_aerosol.F90:78allocate(volcaero_constituents(0))

Suggested pattern (matching the checked allocate calls already used elsewhere in these files, e.g. nucleate_ice_ccpp.F90:153):

allocate(size_wght(ncol, pver, nbins_, nmaxspc_), stat=errflg, errmsg=errmsg)
if (errflg /= 0) return

Rule 1 — No implicit save via initialization on module-level variables

Module-level variables initialized in their declaration acquire an implicit save. Since these are all (re)assigned in the corresponding _init phase, the initializer can simply be dropped to satisfy the rule.

  • schemes/chemistry/rebin_seasalt.F90:27logical :: has_sslt = .false.
  • schemes/microp_aero/ccpp/nucleate_ice_ccpp.F90 — lines 15, 16, 30, 31, 42, 45, 46, 49, 50 (use_preexisting_ice_, nucleate_ice_use_troplev_, clim_modal_aero_, clim_carma_aero_, iaermod_selected_, clim_modal_carma_, prog_modal_aero_, nbins_, nmaxspc_)
  • schemes/sima_diagnostics/aerosol_optics_diagnostics.F90:19integer :: num_odv = 0
  • schemes/sima_diagnostics/ndrop_bam_diagnostics.F90:15integer :: num_aer = 0

Example fix:

logical :: has_sslt      ! set in rebin_seasalt_init

(Note: the parameter constants such as wgt_sscm, mincld_, and qsmall are fine — the rule only concerns saved variables.)


Rule 11 — Character arguments should use len=*

These intent(in)/intent(out) character dummy arguments are declared with a fixed length and are not allocatable/pointer, so they should be len=*:

  • schemes/chemistry/aerosol_optics.F90:47character(len=512), intent(in) :: water_refindex_filecharacter(len=*), intent(in)
  • schemes/sima_diagnostics/aerosol_optics_diagnostics.F90:31character(len=512), intent(out) :: errmsgcharacter(len=*), intent(out)
  • schemes/sima_diagnostics/aerosol_optics_diagnostics.F90:211character(len=512), intent(out) :: errmsgcharacter(len=*), intent(out)
  • test/test_schemes/initialize_pbuf_aerosols.F90:49character(len=512), intent(out) :: errmsgcharacter(len=*), intent(out)

(Most other schemes in this PR already declare errmsg as character(len=*), so this also improves consistency.)


Rule 6 — Namelist XML files must include the standard header comment block

Two of the three added namelist XML files are missing the standard <!-- Each namelist variable is defined in an <entry> element ... --> header block:

  • schemes/microp_aero/ccpp/hetfrz_classnuc_stub_namelist.xml — no header comment
  • schemes/microp_aero/ccpp/nucleate_ice_ccpp_namelist.xml — no header comment

schemes/chemistry/aerosol_optics_namelist.xml includes the block correctly and can be used as the template.


Rule 3 — Label pure where applicable (suggestions)

The following routines have no I/O and modify only their dummy arguments (reading module variables is permitted in a pure procedure), so they appear eligible to be declared pure:

  • schemes/chemistry/aerosol_optics_zero_stub.F90:17aerosol_optics_zero_stub_run (only zeroes its intent(out) arrays)
  • schemes/chemistry/rebin_seasalt.F90:150rebin_seasalt_run

Please confirm against the CCPP framework's expectations for entry-point routines before applying.


Rule 9 — Comment typo (minor)

  • schemes/microp_aero/ccpp/nucleate_ice_ccpp.F90:574 — "wave driven varaibility" → "variability".

Rules with no violations found

Rules 2 (minimal-scope use), 4 (end do/end if — the enddo/endif occurrences in diffusion_solver.F90 are pre-existing and outside this PR's diff), 5 (modern operators — the .ne. in diffusion_solver.F90:823 is likewise pre-existing), 8 (citation DOIs), 10 (single-output subroutines — all changed entry points carry the required errmsg/errflg outputs), and 12 (goto) had no violations in the changed code.


Generated by an automated review against REVIEW.md. Please treat the suggestions above as advisory and verify each against framework constraints.

@jimmielin

Copy link
Copy Markdown
Collaborator Author

Thanks @nusbaume - automated comments resolved

Comment on lines +126 to +128
write(iulog,*) subname//': did not find sslt1/2/3/4 and will not rebin seasalt to SSLTA/SSLTC'
end if
return

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note that this is intended to be non-fatal. It is used for BAM only.

!
! Safe to use with use_hetfrz_classnuc=.true. for snapshot-testing flows where
! the flag must propagate downstream; a warning is printed at init time.
module hetfrz_classnuc_stub

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stub serves both microp_aero and PUMAS, so whichever goes in first.

Comment on lines +133 to +135
call ndrop_bam_calc( &
aero_state = aero_state_bam, &
aero_props = aero_props_bam, &

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contraption brought to you because the framework is currently unable to handle abstract classes like this being passed into the schemes; also, we need to resolve the BAM aerosol state/props from all the potentially active states (more than one can be active at a time.)

This pattern of having a CCPP marshal will be predominant in the BAM/MAM work in the future.

!! \htmlinclude ndrop_bam_diagnostics_init.html
subroutine ndrop_bam_diagnostics_init(errmsg, errflg)
use cam_history, only: history_add_field
use ndrop_bam, only: aername

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no elegant way to get the bulk aerosol names but sourcing it from the portable code is the best I could think of. The other way would be to scan rad_aer_climate from the host radiative_aerosol_definitions which seems more hassle. This is consistent with how CAM does the addflds.

intent = in
[ tke ]
standard_name = specific_turbulent_kinetic_energy_at_interfaces
standard_name = tke_at_interfaces

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name updated to match UW PBL. Missed this in the last PR.

Comment on lines +850 to +851
index(cnst_name, 'NUMLIQ') > 0 .or. & ! micro_pumas_cam if modal/trop_strat carma
index(cnst_name, 'mass_number_concentration_of_cloud_liquid_wrt_moist_air_and_condensed_water') > 0) then

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are one and the same, but before it was NUMLIQ if it was not in the registry; if it is in the registry, it becomes the actual standard name.

I would not spend too much pondering the utility of this subroutine; it will be eliminated after dropmixnuc is CCPPized, and this is supposed to be a custom constituent property, not a logical array.

Comment on lines +1 to +5
! Unified CCPP scheme for ice nucleation via the nucleati kernel.
! Uses the abstract interface to support BAM, MAM, and CARMA.
!
! This scheme replaces nucleate_ice_cam_calc for CAM-SIMA.
module nucleate_ice_ccpp

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern as ndrop_bam_ccpp.

This module supports all aerosol models (BAM/MAM/CARMA)

…bed schemes; diagnostic names in sima_state_diagnostics) into bulk_aero_3_rebased_on_main
…e interface.

The diagnostic-list water uptake in modal_aerosol_state (CAM-SIMA) takes
t, pmid, h2ommr, cldn and top_lev; thread them through the scheme and
the two per-species diagnostic calls.
In CAM these are pbuf fields (qqcw) and never reach the diffusion
constituent loop; CAM-SIMA registers them as constituents, so they must
be excluded by name like the interstitial modal species.
Mirrors physics_update's qneg3 label in CAM ('nucleatei').
…he MAM-side prescribed_stratospheric_aerosol.
CAM computes relative humidity for the optics table lookup inline in
aerosol_optics_cam; in CAM-SIMA cam4/cam5 it comes from
compute_cloud_fraction, which cam7 does not run, so capgen found no
provider for relative_humidity. The water vapor constituent is resolved
by standard name instead of CAM's fixed constituent index 1.
The cam7 cap declares scheme locals by local name, so nucleate_ice's ni
(cloud ice number concentration) collides with the gravity wave drag
schemes' ni (interface Brunt-Vaisala frequency) and the cap fails to
compile. Local-name collisions across schemes with different standard
names are a capgen issue to raise upstream; renaming ours unblocks cam7.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aerosols Is related to aerosols (including aerosol physics and chemistry). enhancement New feature or request

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

CCPPize microp_aero.F90 (BAM path), nucleate_ice_cam.F90 (BAM path), ndrop_bam.F90 CCPPize sslt_rebin

3 participants